package org.monkey.request.plugin.advisor;


import cn.hutool.json.JSONUtil;

import com.fasterxml.jackson.databind.ser.PropertyFilter;
import java.io.File;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import javax.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.aopalliance.aop.Advice;
import org.aopalliance.intercept.MethodInvocation;
import org.monkey.request.plugin.util.IpKit;
import org.monkey.request.plugin.aspect.LogAnnotationParser;
import org.monkey.request.plugin.config.RequestInfoContext;
import org.monkey.request.plugin.entity.RequestInfo;
import org.monkey.request.plugin.entity.ResLog;
import org.monkey.request.plugin.service.RequestLogHandleContext;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

@Slf4j
public class RequestLogAdvice implements Advice {
  private static final Map<String,LogAnnotationParser> RES_LOG_MAP = new ConcurrentHashMap<>();
  private RequestLogHandleContext requestLogHandleContext;

  public RequestLogAdvice(RequestLogHandleContext requestLogHandleContext) {
    this.requestLogHandleContext = requestLogHandleContext;
  }


  public void doFinalAfter() {
    requestLogHandleContext.doAfter(RequestInfoContext.get());
    RequestInfoContext.remove();
  }

  public void doReturn(Object response) {
    try {
      RequestInfo requestInfo = RequestInfoContext.get();
      requestInfo.setEndTime(new Date());
      if (!RequestInfoContext.getParser().isDisable() &&RequestInfoContext.getParser().isResponseParse()) {
        try {
          String returnValue = "";
          if (response instanceof Throwable) {
            Throwable throwable = (Throwable) response;
            requestInfo.setResponse(throwable.getMessage());
          } else {
            returnValue = JSONUtil.toJsonStr(response);
            requestInfo.setResponse(returnValue);
          }
          RequestInfoContext.add(requestInfo,null);
        } catch (Exception e) {
          if (e.getMessage() != null && e.getMessage().contains("write javaBean error")) {
            log.info("文件请求日志,跳过...");
          } else {
            log.info(e.getMessage());
            log.info("用户操作日志拦截器异常");
          }
        }
      }
    }catch (Exception e){
      log.error("请求日志返回处理异常：",e);
    }
  }

  public HttpServletRequest getRequest() {
    RequestAttributes ob = RequestContextHolder.getRequestAttributes();
    if (ob != null) {
      ServletRequestAttributes sr = (ServletRequestAttributes) ob;
      return sr.getRequest();
    } else {
      return null;
    }
  }



  private void doBeforeMethod(MethodInvocation inv) {
    try {
      Object[] args = inv.getArguments();
      Method method = inv.getMethod();
      RequestInfo requestInfo = new RequestInfo();
      requestInfo.setStartTime(new Date());
      String key = method.toGenericString()+"/"+args.length;
      log.info("key");
      LogAnnotationParser lp =RES_LOG_MAP.get(key);
      if(lp==null) {
        ResLog resLog = parseAno(method);
        lp = new LogAnnotationParser(resLog);
        RES_LOG_MAP.put(key,lp);
      }
      requestInfo.setTags(lp.getTags());
      if (!lp.isDisable() && lp.isRequestParse()) {
        HttpServletRequest request = getRequest();
        if (request != null) {
          String url = request.getRequestURI();
          String ip = IpKit.getRealIp(request);
          if (lp.isHeaderParse()) {
            Map<String, String> map = new HashMap<>();
            Enumeration<String> headerNames = request.getHeaderNames();
            while (headerNames.hasMoreElements()) {
              String headName = headerNames.nextElement();
              map.put(headName, request.getHeader(headName));
            }
            requestInfo.setHeaders(map);
          }
          requestInfo.setMethodName(method.getName());
          requestInfo.setIp(ip);
          requestInfo.setHttpMethod(request.getMethod());
          requestInfo.setUrl(url);
          String params = JSONUtil.toJsonStr(args);
          requestInfo.setRequest(params);
          requestLogHandleContext.doPre(requestInfo);
          RequestInfoContext.add(requestInfo,lp);
        } else {
          log.info("请求为空....");
        }
      }
    }catch (Exception e){
      log.error("请求日志预异常：",e);
    }
  }

  private ResLog parseAno(Method method) {
    try {
      ResLog rl= method.getAnnotation(ResLog.class);
      if(rl==null){
        log.info("{}方法上没有ResLog注解",method.getName());
      }
      return rl;
    } catch (Exception e) {
      log.error("获取注解实例失败:",e);
    }
    return null;
  }


  public Object invoke(MethodInvocation methodInvocation) throws Throwable {
      doBeforeMethod(methodInvocation);
      try {
        Object o = methodInvocation.proceed();
        doReturn(o);
        return o;
      }catch (Exception e){
        doReturn(e);
        throw e;
      }finally {
        doFinalAfter();
      }
  }

  public static void main(String[] args) {
    File file = new File("D:\\problem\\1087.xlsx");
    System.out.println(JSONUtil.toJsonStr(file));
  }
}
